﻿namespace HZY.Framework.Repository.EntityFramework;

/// <summary>
/// EntityFrameworkRepositories 工具类
/// </summary>
public static class RepositoryEntityFrameworkExtensions
{
    /// <summary>
    /// 实体 和 dbContext 集合
    /// </summary>
    private static readonly List<DbContextAndEntityInfo> DbContextAndEntityInfos = [];

    /// <summary>
    /// 审计对象
    /// </summary>
    private static readonly List<AuditOptions> AuditOptionList = [];

    /// <summary>
    /// 审计对象
    /// </summary>
    public static List<AuditOptions> AuditOptions => AuditOptionList;

    /// <summary>
    /// FreeSql 对象
    /// </summary>
    public static readonly Dictionary<Type, IFreeSql> FreeSqlOrmList = [];

    /// <summary>
    /// 数据字典映射拦截器
    /// </summary>
    public static Type? DataDictionaryMappingInterceptType { get; private set; }

    static RepositoryEntityFrameworkExtensions()
    {
    }

    /// <summary>
    /// 缓存 dbContext 类型
    /// </summary>
    /// <param name="entityType"></param>
    /// <param name="dbContextType"></param>
    public static void AddDbContextAndEntityInfo(Type entityType, Type dbContextType)
    {
        //验证是否已经存在
        if (DbContextAndEntityInfos.Any(a =>
                a.EntityType.FullName == entityType.FullName && a.DbContextType.FullName == dbContextType.FullName))
            return;

        DbContextAndEntityInfos.Add(new DbContextAndEntityInfo(entityType, dbContextType));
    }

    /// <summary>
    /// 获取缓存的 dbContext 类型
    /// </summary>
    /// <returns></returns>
    public static List<DbContextAndEntityInfo> GetDbContextAndEntityInfoAll() => DbContextAndEntityInfos;

    /// <summary>
    /// 获取所有的 dbContext
    /// </summary>
    /// <returns></returns>
    public static List<Type> GetDbContextTypeAll() =>
        DbContextAndEntityInfos.Select(s => s.DbContextType).Distinct().ToList();

    #region EntityFrameworkRepositories

    /// <summary>
    /// 添加监控 EntityFramework
    /// </summary>
    /// <param name="dbContextOptionsBuilder"></param>
    /// <param name="isMonitor"></param>
    /// <returns></returns>
    public static DbContextOptionsBuilder AddEntityFrameworkMonitor(
        this DbContextOptionsBuilder dbContextOptionsBuilder, bool isMonitor = false)
    {
        dbContextOptionsBuilder.AddInterceptors(new ShardingDbCommandInterceptor());

        //注册监控程序
        if (isMonitor)
        {
            dbContextOptionsBuilder.AddInterceptors(new MonitorDbConnectionInterceptor());
            dbContextOptionsBuilder.AddInterceptors(new MonitorDbCommandInterceptor());
            dbContextOptionsBuilder.AddInterceptors(new MonitorDbTransactionInterceptor());
        }

        return dbContextOptionsBuilder;
    }

    /// <summary>
    /// 注册 EntityFrameworkRepositories
    /// 主要用于注册动态仓储
    /// </summary>
    /// <param name="services"></param>
    /// <param name="repositoryOptions"></param>
    /// <param name="auditOptionsCallBack">审计信息配置</param>
    /// <param name="freeSqlOptionsCallBack">freesql 构建回调</param>
    /// <param name="dataDictionaryMappingIntercept">用于拦截字典数据映射，查询返回结果集</param>
    /// <param name="serviceLifetime"></param>
    /// <returns></returns>
    public static IServiceCollection AddEntityFrameworkRepositories<TDbContext>(
        this IServiceCollection services,
        RepositoryOptions repositoryOptions,
        Action<List<AuditOptions>>? auditOptionsCallBack = null,
        Action<FreeSqlOptions>? freeSqlOptionsCallBack = null,
        Type? dataDictionaryMappingIntercept = null,
        ServiceLifetime serviceLifetime = ServiceLifetime.Scoped)
        where TDbContext : DbContext
    {
        DataDictionaryMappingInterceptType =
            dataDictionaryMappingIntercept ?? typeof(DefaultDataDictionaryMappingIntercept);

        var dbContextType = typeof(TDbContext);

        List<AuditOptions> auditOptions = [];

        #region 添加系统默认标准审计字段

        // 审计字段 - 标准版本
        auditOptions.Add(new AuditOptions()
        {
            CreationTimeFieldName = nameof(ICreateAndUpdateEntity.CreationTime),
            CreatorUserIdFieldName = nameof(ICreateAndUpdateEntity.CreatorUserId),
            LastModificationTimeFieldName = nameof(ICreateAndUpdateEntity.LastModificationTime),
            LastModifierUserIdFieldName = nameof(ICreateAndUpdateEntity.LastModifierUserId),
            DeleterUserIdFieldName = nameof(IDeleteEntity.DeleterUserId),
            DeletionTimeFieldName = nameof(IDeleteEntity.DeletionTime),
            IsDeletedFieldName = nameof(IDeleteEntity.IsDeleted),
        });
        // 审计字段 - 标准版本v2
        auditOptions.Add(new AuditOptions()
        {
            CreationTimeFieldName = nameof(ICreateAndUpdateEntityV2.CreateTime),
            CreatorUserIdFieldName = nameof(ICreateAndUpdateEntityV2.CreateBy),
            LastModificationTimeFieldName = nameof(ICreateAndUpdateEntityV2.UpdateTime),
            LastModifierUserIdFieldName = nameof(ICreateAndUpdateEntityV2.UpdateBy),
            DeletionTimeFieldName = nameof(IDeleteEntityV2.DeleteTime),
            DeleterUserIdFieldName = nameof(IDeleteEntityV2.DeleteBy),
            IsDeletedFieldName = nameof(IDeleteEntityV2.DeleteFlag),
        });

        auditOptionsCallBack?.Invoke(auditOptions);
        AuditOptionList.AddRange(auditOptions);

        #endregion

        var dbContextConfigAttribute = dbContextType.GetCustomAttribute<DbContextConfigAttribute>()!;

        UseEntityFrameworkRepositories(dbContextType);

        // 如果上下文没有配置 DbContextConfigAttribute 则使用默认的扫描方式 DbSet<> 注册
        if (dbContextConfigAttribute != null)
        {
            // 将实体存储起来
            var modelTypes = dbContextConfigAttribute.GetModelTypes(dbContextType);
            foreach (var item in modelTypes)
            {
                AddDbContextAndEntityInfo(item, dbContextType);
            }
        }

        // 将实体与仓储接口注入到 ioc
        GetDbContextAndEntityInfoAll()
            .Where(w => w.DbContextType.FullName == dbContextType.FullName)
            .ToList()
            .ForEach(item =>
            {
                var interfaceType = typeof(IRepository<>).MakeGenericType(item.EntityType);
                var implType = typeof(RepositoryImpl<,>).MakeGenericType(item.EntityType, dbContextType);

                switch (serviceLifetime)
                {
                    case ServiceLifetime.Transient:
                        services.AddTransient(interfaceType, implType);
                        break;
                    case ServiceLifetime.Singleton:
                        services.AddSingleton(interfaceType, implType);
                        break;
                    default:
                        services.AddScoped(interfaceType, implType);
                        break;
                }
            });

        #region freesql

        if (freeSqlOptionsCallBack is null)
        {
            return services;
        }

        // 处理 FreeSql
        if (FreeSqlOrmList.ContainsKey(dbContextType))
        {
            return services;
        }

        FreeSqlOptions freeSqlOptions = new();
        freeSqlOptionsCallBack.Invoke(freeSqlOptions);

        var dataType = repositoryOptions.DefaultDatabaseType switch
        {
            DefaultDatabaseType.SqlServer => FreeSql.DataType.SqlServer,
            DefaultDatabaseType.MySql => FreeSql.DataType.MySql,
            DefaultDatabaseType.PostgreSql => FreeSql.DataType.PostgreSQL,
            DefaultDatabaseType.Oracle => FreeSql.DataType.Oracle,
            _ => FreeSql.DataType.SqlServer
        };

        //else if (repositoryOptions.DefaultDatabaseType == DefaultDatabaseType.Sqlite)
        //{
        //    dataType = FreeSql.DataType.Sqlite;
        //}

        var freeSqlBuilder = new FreeSqlBuilder()
                .UseConnectionString(dataType, repositoryOptions.ConnectionString)
                .UseAutoSyncStructure(false) //自动迁移实体的结构到数据库
                .UseNameConvert(NameConvertType.None)
            //.UseMonitorCommand(cmd => Console.WriteLine($"FreeSql：{cmd.CommandText}"))//监听SQL语句
            ;

        freeSqlOptions.FreeSqlBuilderAction?.Invoke(freeSqlBuilder);

        var freeSql = freeSqlBuilder.Build(); //请务必定义成 Singleton 单例模式

        // Aop
        //freeSql.Aop.CurdAfter += (s, curdAfter) =>
        //{
        //    //if (curdAfter.ElapsedMilliseconds > 1000)
        //    {
        //        var stringBuilder = new StringBuilder();
        //        stringBuilder.Append($"\r\n====[FreeSql 开始 耗时: {curdAfter.ElapsedMilliseconds} ms]=========");
        //        stringBuilder.Append($"\r\n{curdAfter.Sql}");
        //        stringBuilder.Append($"\r\n====[FreeSql 结束 线程Id:{Environment.CurrentManagedThreadId}]=========");
        //        Console.WriteLine(stringBuilder);
        //        //LogUtil.Log.Warning(stringBuilder.ToString());
        //    }
        //};
        // Aop 审计
        freeSql.Aop.AuditValue += (s, auditInfo) =>
        {
            if (freeSqlOptions.FreeSqlAuditAopList.Count > 0)
            {
                freeSqlOptions.FreeSqlAuditAopList.ForEach(a => { a.OnAudit(auditInfo, s); });
            }
        };

        // 实体配置
        freeSql.Aop.ConfigEntity += (s, e) =>
        {
            // 从实体查询 特性 TableAttribute
            var tableAttribute = e.EntityType.GetCustomAttribute<TableAttribute>();
            if (tableAttribute != null)
            {
                e.ModifyResult.Name = tableAttribute.Name;
                return;
            }

            // 从实体查询 特性 EntityDescriptionAttribute
            var entityDescriptionAttribute = e.EntityType.GetCustomAttribute<TableNameAttribute>();
            if (entityDescriptionAttribute != null)
            {
                if (entityDescriptionAttribute.NameRuleMode is NameRuleMode.Default or NameRuleMode.TableName)
                {
                    e.ModifyResult.Name =
                        EntityFrameworkUtil.GetNameByNameRuleType(e.EntityType.Name,
                            entityDescriptionAttribute.NameRuleType); // 字段名
                    return;
                }

                e.ModifyResult.Name = e.EntityType.Name;
                return;
            }

            if (dbContextConfigAttribute is not null)
            {
                e.ModifyResult.Name =
                    EntityFrameworkUtil.GetNameByNameRuleType(e.EntityType.Name,
                        dbContextConfigAttribute.NameRuleType); //表名
            }
        };

        // 实体属性配置
        freeSql.Aop.ConfigEntityProperty += (s, e) =>
        {
            // 处理枚举类型
            if (e.Property.PropertyType.IsEnum)
            {
                e.ModifyResult.MapType = typeof(int);
            }

            // 从字段查询 特性 ColumnAttribute
            var columnAttribute = e.Property.GetCustomAttribute<ColumnAttribute>();
            if (columnAttribute != null && !string.IsNullOrWhiteSpace(columnAttribute.Name))
            {
                e.ModifyResult.Name = columnAttribute.Name;
                return;
            }

            // 从实体查询 特性 EntityDescriptionAttribute
            var entityDescriptionAttribute = e.EntityType.GetCustomAttribute<TableNameAttribute>();
            if (entityDescriptionAttribute is not null)
            {
                if (entityDescriptionAttribute.NameRuleMode is NameRuleMode.Default or NameRuleMode.FieldName)
                {
                    e.ModifyResult.Name =
                        EntityFrameworkUtil.GetNameByNameRuleType(e.Property.Name,
                            entityDescriptionAttribute.NameRuleType); // 字段名
                    return;
                }

                e.ModifyResult.Name = e.Property.Name;
                return;
            }

            if (dbContextConfigAttribute is not null)
            {
                e.ModifyResult.Name =
                    EntityFrameworkUtil.GetNameByNameRuleType(e.Property.Name,
                        dbContextConfigAttribute.NameRuleType); //表名
            }
        };

        freeSqlOptions.FreeSqlAction?.Invoke(freeSql);

        FreeSqlOrmList[dbContextType] = freeSql; //请务必定义成 Singleton 单例模式

        #endregion

        return services;
    }

    /// <summary>
    /// 使用 EntityFrameworkRepositories
    /// </summary>
    /// <param name="dbContextTypes">数据上下文类型</param>
    /// <returns></returns>
    private static void UseEntityFrameworkRepositories(params Type[] dbContextTypes)
    {
        foreach (var item in dbContextTypes)
        {
            //扫描类型下面的 dbSet model
            var propertyInfos =
                item.GetProperties(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);

            var dbSets = propertyInfos.Where(w => w.PropertyType.Name == "DbSet`1");

            foreach (var dbSet in dbSets)
            {
                if (dbSet.PropertyType.GenericTypeArguments.Length <= 0) continue;

                var model = dbSet.PropertyType.GenericTypeArguments[0];
                AddDbContextAndEntityInfo(model, item);
            }
        }
    }

    #endregion
}